package com.ts.utils;

import com.alibaba.fastjson.JSON;
// 常量”UTF-8“
import com.google.common.base.Charsets;
/**
 * <dependency>
 *   <groupId>com.google.guava</groupId>
 *   <artifactId>guava</artifactId>
 *   <version>18.0</version>
 * </dependency>
 * 用于md5计算，不方便引包可自行实现
 */
import com.google.common.hash.Hashing;
import org.apache.commons.lang.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;

/**
 * 签名算法生成规则：该类公共方法谨慎调用，如需签名生成请使用签名建造器 
 *
 * @author Coder.JiA
 * @date 16/1/2020 10:40 AM
 */
public class SignUtil {

    private static final String SECRET_STR_PREFIX = "apigs";
    private static final String SECRET_STR_CONNECTOR = "_";
    private static final String SECRET_STR_SEPARATOR = "@SEPARATOR@";

    /**
     * “未开启防重放攻击”  签名生成策略
     * @param url 地址path信息
     * @param token 安全认证token
     * @param signTypeEnum 
     */
    public static String genSign(String url, Map<String, Object> body, String token, HmacSignTypeEnum signTypeEnum) throws Exception {

        // 明文串
        String plainStr = genPlain(url, body);
        // 密文
        String secretStr = genSecret(token);
        // 生成签名
        String sign = hmacSha(signTypeEnum, plainStr, secretStr);
        return sign;
    }

    /**
     * “开启防重放攻击”  签名生成策略
     * @param caTime 时间戳
     * @param caNonce 随机串
     * @param url 地址path信息
     * @param token 安全认证token
     * @param signTypeEnum
     */
    public static String genSign(long caTime, String caNonce, String url, Map<String, Object> body, String token, HmacSignTypeEnum signTypeEnum) throws Exception {

        // 初始化url
        url = initUrl(caTime, caNonce, url);
        System.out.println(url);
        // 明文串
        String plainStr = genPlain(url, body);
        System.out.println(plainStr);
        // 生成密文
        String secretStr = genSecret(token);
        System.out.println(secretStr);
        // 生成签名
        String sign = hmacSha(signTypeEnum, plainStr, secretStr);
        System.out.println(sign);
        return sign;
    }

    private static String initUrl(long caTime, String caNonce, String url) {
        if (url.contains("?")){
            url += "&";
        } else {
            url += "?";
        }
        url += "apigs_ca_time" + "=" + caTime + "&" + "apigs_ca_nonce" + "=" + caNonce;
        return url;
    }

    private static String genPlain(String url, Map<String, Object> body) {
        String ret = "[url]=" + url;
        if (null != body) {
            ret +=  SECRET_STR_SEPARATOR + "[body]=" + JSON.toJSONString(body).replaceAll("\\s+", "");
        }
        return ret;
    }

    private static String genSecret(String token) {
        // 密文
        String secretStr = SECRET_STR_PREFIX;
        if (StringUtils.isNotBlank(token)) {
            secretStr += SECRET_STR_CONNECTOR + md5(token);
        }
        return secretStr;
    }

    private static String hmacSha(HmacSignTypeEnum signTypeEnum, String data, String key) throws Exception {

        Mac hmacSha = Mac.getInstance(signTypeEnum.getName());

        // 密文生成
        byte[] keyBytes = key.getBytes(Charsets.UTF_8); //"UTF-8"
        hmacSha.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, signTypeEnum.getName()));

        // 开始加密
        byte[] bytes = hmacSha.doFinal(data.getBytes(Charsets.UTF_8)); //"UTF-8"
        StringBuilder ret = new StringBuilder();
        for (byte b : bytes) {
            ret.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1, 3));
        }

        return ret.toString();
    }

    /**
     * md5,可自行实现md5
     * 
     * @param plainText
     * @return
     */
    private static String md5(String plainText) {
        return Hashing.md5().newHasher().putString(plainText, Charsets.UTF_8).hash().toString(); //"UTF-8"
    }

}
